package block

import (
	"bytes"
	"encoding/json"
	"github.com/corgi-kx/blockchain_golang/database"
	log "github.com/corgi-kx/logcustom"
)

type blockchain struct {
	BD *database.BlockchainDB //封装的blot结构体
}

//创建区块链实例
func NewBlockchain() *blockchain {
	blockchain := blockchain{}
	bd := database.New()
	blockchain.BD = bd
	return &blockchain
}

//创建挖矿奖励地址交易
func (bc *blockchain) CreataRewardTransaction(address string) Transaction {
	if address == "" {
		log.Warn("没有设置挖矿奖励地址，如果出块则不会给予奖励代币")
		return Transaction{}
	}
	if !IsVaildBitcoinAddress(address) {
		log.Warnf("奖励地址格式不正确:%s\n", address)
		return Transaction{}
	}

	publicKeyHash := GetPublicKeyHashFromAddress(address)
	txo := TXOutput{TokenRewardNum, publicKeyHash}
	ts := Transaction{nil, nil, []TXOutput{txo}}
	ts.Hash()
	return ts
}

//创建UTXO交易实例
func (bc *blockchain) CreateTransaction(from, to string, amount string, send Sender) []Transaction {
	//判断一下是否已生成创世区块
	//if len(bc.BD.View([]byte(LastBlockHashMapping), database.BlockBucket)) == 0 {
	//	log.Error("还没有生成创世区块，不可进行转账操作 !")
	//	return
	//}
	//检测是否设置了挖矿地址,没设置的话会给出提示
	//if len(bc.BD.View([]byte(RewardAddrMapping), database.AddrBucket)) == 0 {
	//	log.Warn("没有设置挖矿地址，如果挖出区块将不会给予奖励代币!")
	//}

	fromSlice := []string{}
	toSlice := []string{}
	amountSlice := []int{}

	//对传入的信息进行校验检测
	err := json.Unmarshal([]byte(from), &fromSlice)
	if err != nil {
		log.Error("json err:", err)
		return nil
	}
	err = json.Unmarshal([]byte(to), &toSlice)
	if err != nil {
		log.Error("json err:", err)
		return nil
	}
	err = json.Unmarshal([]byte(amount), &amountSlice)
	if err != nil {
		log.Error("json err:", err)
		return nil
	}
	if len(fromSlice) != len(toSlice) || len(fromSlice) != len(amountSlice) {
		log.Error("转账数组长度不一致")
		return nil
	}

	for i, v := range fromSlice {
		if !IsVaildBitcoinAddress(v) {
			log.Errorf(" %s,地址格式不正确！已将此笔交易剔除\n", v)
			if i < len(fromSlice)-1 {
				fromSlice = append(fromSlice[:i], fromSlice[i+1:]...)
				toSlice = append(toSlice[:i], toSlice[i+1:]...)
				amountSlice = append(amountSlice[:i], amountSlice[i+1:]...)
			} else {
				fromSlice = append(fromSlice[:i])
				toSlice = append(toSlice[:i])
				amountSlice = append(amountSlice[:i])
			}
		}
	}

	for i, v := range toSlice {
		if !IsVaildBitcoinAddress(v) {
			log.Errorf(" %s,地址格式不正确！已将此笔交易剔除\n", v)
			if i < len(fromSlice)-1 {
				fromSlice = append(fromSlice[:i], fromSlice[i+1:]...)
				toSlice = append(toSlice[:i], toSlice[i+1:]...)
				amountSlice = append(amountSlice[:i], amountSlice[i+1:]...)
			} else {
				fromSlice = append(fromSlice[:i])
				toSlice = append(toSlice[:i])
				amountSlice = append(amountSlice[:i])
			}
		}
	}
	for i, v := range amountSlice {
		if v < 0 {
			log.Error("转账金额不可小于0，已将此笔交易剔除")
			if i < len(fromSlice)-1 {
				fromSlice = append(fromSlice[:i], fromSlice[i+1:]...)
				toSlice = append(toSlice[:i], toSlice[i+1:]...)
				amountSlice = append(amountSlice[:i], amountSlice[i+1:]...)
			} else {
				fromSlice = append(fromSlice[:i])
				toSlice = append(toSlice[:i])
				amountSlice = append(amountSlice[:i])
			}
		}
	}

	var tss []Transaction
	wallets := NewWallets(bc.BD)
	for index, fromAddress := range fromSlice {
		fromKeys, ok := wallets.Wallets[fromAddress]
		if !ok {
			log.Errorf("没有找到地址%s所对应的公钥,跳过此笔交易", fromAddress)
			continue
		}
		toKeysPublicKeyHash := GetPublicKeyHashFromAddress(toSlice[index])
		if fromAddress == toSlice[index] {
			log.Errorf("相同地址不能转账！！！:%s\n", fromAddress)
			return nil
		}

		//获取云计算节点中的未消费的utxo
		u := UTXOHandle{bc}
		//utxos := u.findUTXOFromAddress(fromAddress)
		utxosBytes := send.GetUTXOsBytes(fromAddress)
		utxos := u.dserialize(utxosBytes)

		if len(utxos) == 0 {
			log.Errorf("%s 余额为0,不能进行转帐操作", fromAddress)
			return nil
		}
		//将utxos添加上未打包进区块的交易信息
		if tss != nil {
			for _, ts := range tss {
				//先添加未花费utxo 如果有的话就不添加
			tagVout:
				for index, vOut := range ts.Vout {
					if bytes.Compare(vOut.PublicKeyHash, generatePublicKeyHash(fromKeys.PublicKey)) != 0 {
						continue
					}
					for _, utxo := range utxos {
						if bytes.Equal(ts.TxHash, utxo.Hash) && index == utxo.Index {
							continue tagVout
						}
					}
					utxos = append(utxos, &UTXO{ts.TxHash, index, vOut})
				}
				//剔除已花费的utxo
				for _, vInt := range ts.Vint {
					for index, utxo := range utxos {
						if bytes.Equal(vInt.TxHash, utxo.Hash) && vInt.Index == utxo.Index {
							utxos = append(utxos[:index], utxos[index+1:]...)
						}
					}
				}

			}
		}

		//打包交易的核心操作
		newTXInput := []TXInput{}
		newTXOutput := []TXOutput{}
		var amount int
		for _, utxo := range utxos {
			amount += utxo.Vout.Value
			newTXInput = append(newTXInput, TXInput{utxo.Hash, utxo.Index, nil, fromKeys.PublicKey})
			if amount > amountSlice[index] {
				tfrom := TXOutput{}
				tfrom.Value = amount - amountSlice[index]
				tfrom.PublicKeyHash = generatePublicKeyHash(fromKeys.PublicKey)
				tTo := TXOutput{}
				tTo.Value = amountSlice[index]
				tTo.PublicKeyHash = toKeysPublicKeyHash
				newTXOutput = append(newTXOutput, tfrom)
				newTXOutput = append(newTXOutput, tTo)
				break
			} else if amount == amountSlice[index] {
				tTo := TXOutput{}
				tTo.Value = amountSlice[index]
				tTo.PublicKeyHash = toKeysPublicKeyHash
				newTXOutput = append(newTXOutput, tTo)
				break
			}
		}
		//如果余额不足则跳过不会打包进入交易
		if amount < amountSlice[index] {
			log.Errorf(" 第%d笔交易%s余额不足", index+1, fromAddress)
			continue
		}
		ts := Transaction{nil, newTXInput, newTXOutput[:]}
		ts.Hash()
		tss = append(tss, ts)
	}
	if tss == nil {
		return nil
	}

	//fmt.Printf("%s",tss)
	bc.signatureTransactions(tss, wallets, send)
	//向P2P节点发送交易数据
	//send.SendTransToPeers(tss)
	return tss
}

//设置挖矿奖励地址
func (bc *blockchain) SetRewardAddress(address string) {
	bc.BD.Put([]byte(RewardAddrMapping), []byte(address), database.AddrBucket)
}

//对交易信息进行数字签名
func (bc *blockchain) signatureTransactions(tss []Transaction, wallets *wallets, send Sender) {
	for i := range tss {
		copyTs := tss[i].customCopy()
		for index := range tss[i].Vint {
			//获取地址
			bk := bitcoinKeys{nil, tss[i].Vint[index].PublicKey, nil}
			address := bk.getAddress()
			//从数据库或者为打包进数据库的交易数组中,找到vint所对应的交易信息

			transbyte := send.GetTrans(tss[i].Vint[index].TxHash)

			trans := DeserializeTransaction(transbyte)

			//trans, err := bc.findTransaction(tss, tss[i].Vint[index].TxHash)
			//先查找未插入数据库的交易
			if len(tss) != 0 {
				for _, tx := range tss {
					if bytes.Compare(tx.TxHash, tss[i].Vint[index].TxHash) == 0 {
						trans = tx
					}
				}
			}

			copyTs.Vint[index].Signature = nil
			//将拷贝后的交易里面的公钥替换为公钥hash
			copyTs.Vint[index].PublicKey = trans.Vout[tss[i].Vint[index].Index].PublicKeyHash
			//对拷贝后的交易进行整体hash
			copyTs.TxHash = copyTs.hashSign()
			copyTs.Vint[index].PublicKey = nil
			privKey := wallets.Wallets[string(address)].PrivateKey
			//进行签名操作
			tss[i].Vint[index].Signature = ellipticCurveSign(privKey, copyTs.TxHash)
		}
	}
}
